/*
 Xern Entity Relocation Network
 Copyright (C) 2010-2011 Ronie Salgado <roniesalg@gmail.com>

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/

#ifndef XERN_DATA_STREAM_HPP
#define XERN_DATA_STREAM_HPP

#include <stdio.h>
#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/uuid/uuid.hpp>
#include "DataTypes.hpp"

namespace Xern
{
	using boost::uuids::uuid;
	namespace detail
	{
		// Value reader.
		template <typename VALUE, bool BIG=false>
		struct ValueReader {};

		template <bool BIG>
		struct ValueReader<u8, BIG>
		{
			template<typename STREAM>
			static bool Read(STREAM &in, u8 &value)
			{
				return in.Read(1, &value);
			}
		};

		template <bool BIG>
		struct ValueReader<u16, BIG>
		{
			template<typename STREAM>
			static bool Read(STREAM &in, u16 &value)
			{
				bool ret = in.Read(2, &value);

				if(!BIG && MachineBigEndian ||
				  BIG && !MachineBigEndian)
				{
					value = ((value & 0xFF00) >> 8) |
							((value & 0x00FF) << 8);
				}

				return ret;
			}
		};

		template <bool BIG>
		struct ValueReader<u32, BIG>
		{
			template<typename STREAM>
			static bool Read(STREAM &in, u32 &value)
			{
				bool ret = in.Read(4, &value);

				if(!BIG && MachineBigEndian ||
				  BIG && !MachineBigEndian)
				{
					value = ((value & 0xFF000000) >> 24) |
							((value & 0x00FF0000) >> 8) |
							((value & 0x0000FF00) << 8) |
							((value & 0x000000FF) << 24);
				}

				return ret;
			}
		};

		template <bool BIG>
		struct ValueReader<u64, BIG>
		{
			template<typename STREAM>
			static bool Read(STREAM &in, u64 &value)
			{
				bool ret = in.Read(8, &value);

				if(!BIG && MachineBigEndian ||
				  BIG && !MachineBigEndian)
				{
					value = ((value & 0xFF00000000000000l) >> 56) |
							((value & 0x00FF000000000000l) >> 40) |
							((value & 0x0000FF0000000000l) >> 24) |
							((value & 0x000000FF00000000l) >> 8) |
							((value & 0x00000000FF000000l) << 8) |
							((value & 0x0000000000FF0000l) << 24) |
							((value & 0x000000000000FF00l) << 40) |
							((value & 0x00000000000000FFl) << 56);
				}
				return ret;
			}
		};

		template <bool BIG>
		struct ValueReader<f32, BIG>
		{
			template<typename STREAM>
			static bool Read(STREAM &in, f32 &value)
			{
				return ValueReader<u32, BIG>::Read(in, reinterpret_cast<u32&> (value));
			}
		};

		template <bool BIG>
		struct ValueReader<f64, BIG>
		{
			template<typename STREAM>
			static bool Read(STREAM &in, f64&value)
			{
				return ValueReader<u64, BIG>::Read(in, reinterpret_cast<u64&> (value));
			}
		};

		template <bool BIG>
		struct ValueReader<std::string, BIG>
		{
			template<typename STREAM>
			static bool Read(STREAM &in, std::string &value)
			{
				std::vector<char> buffer;
				u16 size;
				if(!ValueReader<u16, BIG>::Read(in, size))
					return false;
				buffer.resize(size);
				if(!in.Read(size, &buffer[0]))
					return false;
				value = std::string(buffer.begin(), buffer.end());
				return true;
			}
		};

		template <bool BIG>
		struct ValueReader<uuid, BIG>
		{
			template<typename STREAM>
			static bool Read(STREAM &in, uuid &value)
			{
				return in.Read(16, value.data);
			}
		};

		// Value Writer.
		template <typename VALUE, bool BIG=false>
		struct ValueWriter {};

		template <bool BIG>
		struct ValueWriter<u8, BIG>
		{
			template<typename STREAM>
			static bool Write(STREAM &out, u8 value)
			{
				return out.Write(1, &value);
			}
		};

		template <bool BIG>
		struct ValueWriter<u16, BIG>
		{
			template<typename STREAM>
			static bool Write(STREAM &out, u16 value)
			{
				if(!BIG && MachineBigEndian ||
				  BIG && !MachineBigEndian)
				{
					value =	((value & 0xFF00) >> 8) |
						((value & 0x00FF) << 8);
				}

				return out.Write(2, &value);
			}
		};

		template <bool BIG>
		struct ValueWriter<u32, BIG>
		{
			template<typename STREAM>
			static bool Write(STREAM &out, u32 value)
			{
				if(!BIG && MachineBigEndian ||
				  BIG && !MachineBigEndian)
				{
					value = ((value & 0xFF000000) >> 24) |
						((value & 0x00FF0000) >> 8) |
						((value & 0x0000FF00) << 8) |
						((value & 0x000000FF) << 24);
				}

				return out.Write(4, &value);
			}
		};

		template <bool BIG>
		struct ValueWriter<u64, BIG>
		{
			template<typename STREAM>
			static bool Write(STREAM &out, u64 value)
			{
				if(!BIG && MachineBigEndian ||
				  BIG && !MachineBigEndian)
				{
					value = ((value & 0xFF00000000000000l) >> 56) |
							((value & 0x00FF000000000000l) >> 40) |
							((value & 0x0000FF0000000000l) >> 24) |
							((value & 0x000000FF00000000l) >> 8) |
							((value & 0x00000000FF000000l) << 8) |
							((value & 0x0000000000FF0000l) << 24) |
							((value & 0x000000000000FF00l) << 40) |
							((value & 0x00000000000000FFl) << 56);
				}

				return out.Write(8, &value);
			}
		};

		template <bool BIG>
		struct ValueWriter<std::string, BIG>
		{
			template<typename STREAM>
			static bool Write(STREAM &out, const std::string &value)
			{
				if(!ValueWriter<u16, BIG>::Write(out, value.size()))
					return false;
				return out.Write(value.size(), value.data());
			}
		};

		template <bool BIG>
		struct ValueWriter<uuid, BIG>
		{
			template<typename STREAM>
			static bool Write(STREAM &out, const uuid &value)
			{
				return out.Write(16, value.data);
			}
		};

		template <bool BIG>
		struct ValueWriter<f32, BIG>
		{
			template<typename STREAM>
			static bool Write(STREAM &in, const f32 &value)
			{
				return ValueWriter<u32, BIG>::Write(in, reinterpret_cast<const u32&> (value));
			}
		};

		template <bool BIG>
		struct ValueWriter<f64, BIG>
		{
			template<typename STREAM>
			static bool Write(STREAM &in, const f64 &value)
			{
				return ValueWriter<u64, BIG>::Write(in, reinterpret_cast<const u64&> (value));
			}
		};

		// Signed-unsigned helper.
		template <typename FROM, typename TO, bool BIG>
		struct DelegateReader
		{
			template<typename STREAM>
			static bool Read(STREAM &in, FROM &value)
			{
				return ValueReader<TO, BIG>::Read(in, (TO&)value);
			}
		};

		template <typename FROM, typename TO, bool BIG>
		struct DelegateWriter
		{
			template<typename STREAM>
			static bool Write(STREAM &out, const FROM &value)
			{
				return ValueWriter<TO, BIG>::Write(out, reinterpret_cast<const TO&> (value));
			}
		};

		// Signed versions
		template <bool BIG>
		struct ValueReader<s8, BIG> : DelegateReader<s8, u8, BIG> {};

		template <bool BIG>
		struct ValueReader<s16, BIG> : DelegateReader<s16, u16, BIG> {};

		template <bool BIG>
		struct ValueReader<s32, BIG> : DelegateReader<s32, u32, BIG> {};

		template <bool BIG>
		struct ValueReader<s64, BIG> : DelegateReader<s64, u64, BIG> {};

		template <bool BIG>
		struct ValueWriter<s8, BIG> : DelegateWriter<s8, u8, BIG> {};

		template <bool BIG>
		struct ValueWriter<s16, BIG> : DelegateWriter<s16, u16, BIG> {};

		template <bool BIG>
		struct ValueWriter<s32, BIG> : DelegateWriter<s32, u32, BIG> {};

		template <bool BIG>
		struct ValueWriter<s64, BIG> : DelegateWriter<s64, u64, BIG> {};
	};

	struct IDataFilter;
	class DataStream
	{
	public:
		DataStream(FILE *file, bool own=false);
		DataStream(size_t len, void *buffer);
		DataStream(boost::shared_ptr<IDataFilter> filter);
		~DataStream();

		boost::shared_ptr<IDataFilter> GetFilter();
		void SetFilter(boost::shared_ptr<IDataFilter> filter);

		size_t Read(size_t size, void *buffer);
		size_t Write(size_t size, const void *data);
		size_t Skip(size_t amount);
		bool Flush();
		size_t GetPosition() const;

		template<typename T>
		bool ReadValue(T &value)
		{
			return ::Xern::detail::ValueReader<T>::Read (*this, value);
		}

		template <typename T>
		void WriteValue(const T &value)
		{
			::Xern::detail::ValueWriter<T>::Write (*this, value);
		}

		template<typename T>
		bool ReadBig(T &value)
		{
			return ::Xern::detail::ValueReader<T, true>::Read (*this, value);
		}

		template<typename T, typename V>
		bool ReadBigTo(V &value)
		{
			T t;
			bool ret = ::Xern::detail::ValueReader<T, true>::Read (*this, t);
			value = t;
			return ret;
		}

		template <typename T>
		void WriteBig(const T &value)
		{
			::Xern::detail::ValueWriter<T, true>::Write (*this, value);
		}

		template<typename T>
		bool ReadLittle(T &value)
		{
			return ::Xern::detail::ValueReader<T, false>::Read (*this, value);
		}

		template<typename T, typename V>
		bool ReadLittleTo(V &value)
		{
			T t;
			bool ret = ::Xern::detail::ValueReader<T, false>::Read (*this, t);
			value = t;
			return ret;
		}

		template <typename T>
		void WriteLittle(const T &value)
		{
			::Xern::detail::ValueWriter<T, false>::Write (*this, value);
		}

	private:
		boost::shared_ptr<IDataFilter> filter;
	};

	template<typename T>
	DataStream &operator<<(DataStream &stream, const T &value)
	{
		stream.WriteValue(value);
		return stream;
	}

	template<typename T>
	DataStream &operator>>(DataStream &stream, T &value)
	{
		stream.ReadValue(value);
		return stream;
	}

}

#endif //XERN_DATA_STREAM_HPP
